﻿using Sai.Cecil;
using Sai.Core;
using Sai.Core.Introspection;
using Sai.Core.Rules;
using System.Linq;

namespace Sai.Test.TestDataBuilders
{
    public class TypeDifferenceBuilder
    {
        private string oldCode;
        private string newCode;

        private ITypeRule rule;
        private IProductReleaseProject project;
        private IAssemblyIntrospector introspector = new MonoCecilAssemblyIntrospector();

        public static TypeDifferenceBuilder Differences ()
        {
            return new TypeDifferenceBuilder();
        }

        public TypeDifferenceBuilder CodeWas(string oldCode)
        {
            this.oldCode = oldCode;

            return this;
        }

        public TypeDifferenceBuilder CodeIsNow(string newCode)
        {
            this.newCode = newCode;

            return this;
        }

        public TypeDifferenceBuilder CheckedUsing(ITypeRule rule)
        {
            this.rule = rule;

            return this;
        }

        public TypeDifferenceBuilder WithProject(IProductReleaseProject project)
        {
            this.project = project;

            return this;
        }

        public TypeDifferenceBuilder UsingInspector(IAssemblyIntrospector inspector)
        {
            this.introspector = inspector;

            return this;
        }

        public TypeDifference Build()
        {
            using (OnTheFlyAssembly oldAssembly = new OnTheFlyAssembly())
            {
                oldAssembly.Compile(this.oldCode);

                using (OnTheFlyAssembly newAssembly = new OnTheFlyAssembly())
                {
                    newAssembly.Compile(this.newCode);

                    var oldResult = this.introspector.IntrospectAssembly(oldAssembly.ProductAssembly);
                    var newResult = this.introspector.IntrospectAssembly(newAssembly.ProductAssembly);

                    var collection = oldResult.TypesInCommonWith(newResult);

                    if (collection.Any())
                    {
                        TypeName typeName = collection.First();

                        ITypeDeclaration oldDec = oldResult.Lookup(typeName);
                        ITypeDeclaration newDec = newResult.Lookup(typeName);

                        TypeDifference diff = new TypeDifference();

                        if (this.project == null)
                            this.project = Mock.MinimalProject();

                        diff.ReportOnAdditions = this.project.ReportOnAdditions;
                        diff.ReportOnChanges = this.project.ReportOnChanges;
                        diff.ReportOnObsolete = this.project.ReportOnObsolete;
                        diff.ReportOnRemovals = this.project.ReportOnRemovals;

                        TypeDifferenceWalker walker = new TypeDifferenceWalker();
                        walker.Add(this.rule);

                        walker.Walk(oldDec, newDec, diff);

                        return diff;
                    }

                    return null;
                }
            }
        }

    }
}
